#include "talk/base/thread.h"
#include "talk/examples/crossfirewall/cfcore.h"

using namespace cricket;
using namespace talk_base;


CmdConsole::CmdConsole(void* core)
: core_(core)
{
}

CmdConsole::~CmdConsole()
{
}

void CmdConsole::OnMessage(talk_base::Message* pmsg)
{
	switch(pmsg->message_id)
	{
	// core related commands
	case CMD_LOGIN_INIT_CORE:
		((CFCore*)core_)->OnInitCore();
		break;
	case CMD_LOGIN_UNINIT_CORE:
		((CFCore*)core_)->OnUnInitCore();
		break;
	case CMD_CORE_RELOGIN:
		((CFCore*)core_)->ReLogin();
		break;

	// login related commands
	case CMD_LOGIN_STATE_CHANGE:
		((CFCore*)core_)->OnLoginStateChange(*(const LoginState*)pmsg->pdata);
		delete (LoginState*)pmsg->pdata;
		break;
	case CMD_LOGIN_ROSTER_CHANGE:
		((CFCore*)core_)->OnLoginRosterChange(*(const RosterItem*)pmsg->pdata);
		delete (RosterItem*)pmsg->pdata;
		break;

    //call related commands 
	case CMD_CALL_STATE_CHANGE:
		((CFCore*)core_)->OnCallStateChange(*(const CallState*)pmsg->pdata);
		delete (CallState*)pmsg->pdata;
		break;
	case CMD_CALL_DO_MAKECALL:
		{
			const RosterItem* item = (const RosterItem*)pmsg->pdata;
			((CFCore*)core_)->MakeCall_w(item->jid_.c_str());
			delete (RosterItem*)pmsg->pdata;
		}
		break;
	case CMD_CALL_DO_HANGEUPCALL:
		{
			const RosterItem* item = (const RosterItem*)pmsg->pdata;
			((CFCore*)core_)->HangupCall_w(item->jid_.c_str());
			delete (RosterItem*)pmsg->pdata;
		}	
		break;
	
	case CMD_CALL_DO_DESTROYCALL:
		{
			const RosterItem* item = (const RosterItem*)pmsg->pdata;
			((CFCore*)core_)->DestroyCall_w(item->jid_.c_str());
			delete (RosterItem*)pmsg->pdata;
		}
		break;
	case CMD_CALL_DO_HANGEUPCALL_ALL:
		((CFCore*)core_)->HangupCallAll_w();
		break;

	//xmpp related commands 
	case CMD_XMPP_FLUSH: //only for wake up the xmpp thread (other name login thread)
		((CFCore*)core_)->OnXmppFlush();
		break;
	}
}

CFCore::CFCore()
{
	ls_cb_ = NULL;  rs_cb_ = NULL;  cs_cb_ = NULL;

	cmd_console_     = new CmdConsole(this);
	thread_manager_  = new ThreadManager();
	cmd_thread_      = new Thread();	
		
    cmd_thread_->Start();
	cf_login_.SetCmdWayToCore(cmd_thread_, cmd_console_);
	
	cfcall_mannager_ = NULL;
	SetLoginStatus(LOGIN_STATE_LOGOUT);
}

CFCore::~CFCore()
{
	if(cfcall_mannager_) { delete cfcall_mannager_;  cfcall_mannager_ = NULL; }

	if(cmd_thread_)     { cmd_thread_->Stop();  delete cmd_thread_;  cmd_thread_ = NULL; }
	if(thread_manager_) { delete thread_manager_;  thread_manager_ = NULL; }
	if(cmd_console_)    { delete cmd_console_;     cmd_console_    = NULL; }
}

bool CFCore::Login(const char* usrname, const char* usrpass, const char * computername, const char * resource)
{
	SetLoginStatus(LOGIN_STATE_LOGINING);
	CFUsrInfo usrinfo;
	usrinfo.usrname      = usrname;      usrinfo.usrpass  = usrpass;
	usrinfo.computername = computername; usrinfo.resource = resource;
	return cf_login_.Login(usrinfo);
}

bool CFCore::Logout()
{
	SetLoginStatus(LOGIN_STATE_LOGOUT);
	HangupCallAll();
	return cf_login_.Logout();
}

void CFCore::ReLogin()
{
	cf_login_.LoginRetry();
}

void CFCore::MakeCall(const char* remote_jid)
{
	// use login thread to make call
	Thread* thread = cf_login_.GetLoginThread();
	if(thread) thread->Post(cmd_console_, CMD_CALL_DO_MAKECALL, new RosterItem(remote_jid));
}

void CFCore::MakeCall_w(const char* remote_jid)
{
	// make call
	if(cfcall_mannager_){
		cfcall_mannager_->MakeCall(remote_jid);
		cf_login_.FlushXmpp();
	}
}

void CFCore::HangupCall(const char* remote_jid)
{
	// use login thread to hangup call
	Thread* thread = cf_login_.GetLoginThread();
	if(thread)  thread->Post(cmd_console_, CMD_CALL_DO_HANGEUPCALL, new RosterItem(remote_jid));
}

void CFCore::HangupCall_w(const char* remote_jid)
{
	// endup p2p channel
	if(cfcall_mannager_)
		cfcall_mannager_->HangupCall(remote_jid);

	cf_login_.FlushXmpp();
}

void CFCore::DestroyCall(const char* remote_jid)
{
	// use login thread to hangup call
	Thread* thread = cf_login_.GetLoginThread();
	if(thread)  thread->Post(cmd_console_, CMD_CALL_DO_DESTROYCALL, new RosterItem(remote_jid));
}

void CFCore::DestroyCall_w(const char* remote_jid)
{
	// endup p2p channel
	if(cfcall_mannager_)
		cfcall_mannager_->DestroyCall(remote_jid);	
}

void CFCore::HangupCallAll()
{
	// use login thread to hangup call all
	Thread* thread = cf_login_.GetLoginThread();
	if(thread)  thread->Post(cmd_console_, CMD_CALL_DO_HANGEUPCALL_ALL);	
}

void CFCore::HangupCallAll_w()
{
	// endup p2p channel
	if(cfcall_mannager_)
		cfcall_mannager_->HangupCallAll();
}

void CFCore::OnInitCore()
{
	bool bhttpallocator = false;
#if defined(HTTPALLOCATOR)
	bhttpallocator = true;
	cf_login_.SignalJingleInfo.connect(this, &CFCore::OnLoginJingleInfo);
#endif

	if(cfcall_mannager_) { delete cfcall_mannager_;   cfcall_mannager_ = NULL; }
	cfcall_mannager_  = new cricket::CFCallManager(cf_login_.GetJid(), cf_login_.GetXmppClient(), bhttpallocator);
	cfcall_mannager_->SetCmdWayToCore(cmd_thread_, cmd_console_);
}

void CFCore::OnUnInitCore()
{
	if(cfcall_mannager_) { delete cfcall_mannager_;  cfcall_mannager_ = NULL; }
}

void CFCore::OnLoginRosterChange(const cricket::RosterItem& item)
{
	//STR_LOG(INFO)<<"CFCore---->Roster Change("<<item.jid_.c_str()<<", "<<item.show_<<").";
	if(rs_cb_)
		rs_cb_(item.jid_.c_str(), item.show_, item.version_.c_str(), item.status_.c_str());

	if(item.show_ <= 1)  HangupCall(item.jid_.c_str());
}

void CFCore::OnLoginStateChange(const cricket::LoginState& state)
{
	//STR_LOG(INFO)<<"CFCore---->Login State Change("<<state.state_<<", "<<state.error_<<", "<<str_login_.GetLoginRetrys()<<").";
	if(state.state_ == buzz::XmppEngine::STATE_OPEN){
		SetLoginStatus(LOGIN_STATE_LOGINED);
	}
	else if(state.state_ == buzz::XmppEngine::STATE_CLOSED){
		HangupCallAll();
		cf_login_.Logout();
		cmd_thread_->Clear(cmd_console_, CMD_LOGIN_STATE_CHANGE);
		/*{
			talk_base::CritScope cs(&core_crit_);
			if(login_state_ == LOGIN_STATE_LOGINING && state.error_ != ERROR_UNAUTHORIZED){
				cmd_thread_->PostDelayed(LOGIN_RETRY_DELAY, cmd_console_, CMD_CORE_RELOGIN);
				return;
			}
		}*/
	}

	struct status_msg msg;  msg.status = state.state_;  msg.error  = state.error_;
	if(ls_cb_)  ls_cb_(msg);
}

void CFCore::OnCallStateChange(const cricket::CallState& state)
{
	//STR_LOG(INFO)<<"CFCore---->Call State Change("<<state.jid_.c_str()<<", "<<state.state_<<").";
	switch(state.state_)
	{
	case STR_CALL_STATE_READY:
		break;
	case STR_CALL_STATE_ENDING:
		break;
	case STR_CALL_STATE_ENDED:
		DestroyCall(state.jid_.c_str());
		break;
	}

	if(cs_cb_)  cs_cb_(state.jid_.c_str(), state.state_);
}